表锁
这里表锁我只说MDl锁。也就是元数据锁,这个锁是表锁,其分为mdl读锁,mdl锁就是对一个表做增删改查的时候,加的就是mdl读锁,修改表结构的时候加的则是mdl写锁。
读锁-读锁互不影响。读锁-写锁互斥。写锁-写锁互斥。
当我们修改表结构的时候,对于大表,我们会仔细操作,因为这个操作会导致全表扫描。但是即使对小表修改表结构,也会造成严重影响。
举个例子:
查询,查询,改结构,查询。这4个,后面2个都会阻塞,因为读写互斥。如果后续请求很多,可能大量阻塞卡死。
那么如何安全的修改表结构呢?
查询当前长事务,看看你要变更的表是否在里面,如果在,等长事务结束,或者杀死这个长事务。
行锁
mysql的行锁是是存储引擎实现的。这边我专门说下innodb,因为myisam是不支持行锁的,因此对于高并发下,行数据是不安全的。在innodb里面,行锁是在你需要的时候加上去,比如修改某一行的值。修改完之后不是立刻释放,而是等事务结束后才释放锁。
在并发系统中不同线程出现循环资源依赖将会导致这几个线程进入无限等待的状态,称之为死锁。
下面情况出现死锁:
事务1 对表A的数据a修改
事务2 对表A的数据b修改
事务1 对表A的数据b修改
事务2 对表A的数据a修改
在出现死锁的情况有以下2种情况对其进行处理。
1.进入等待状态,直到超时。超时参数可以通过参数innodb_lock_wait_timeout来设置。默认是50s。一般不采用这个方式。
2.发起死锁检测,发现死锁后,主动回滚死锁链条的某一个事务,让其他事务得以执行。将参数innodb_deadlock_detect设置为0表示开启这个逻辑。我们一般采用这个方法。
但是这个是有代价的。该逻辑如下:每当一个事务被锁住的时候,就要看看它依赖的线程有没有被别人锁住,不断循环,最后判断是否出现循环等待,也就是死锁。
如果是所有事务都是更新同一行的操作,那么死锁检查操作就是每个新来的被堵住的线程都要判断是否是因为自己的到来而导致了死锁,如果是1000的并发,那么其操作则是100w量级的。这样会导致cpu 100%异常。
对于热点行数据更新问题。有以下方案:
1.确定不是死锁,把死锁检查关掉
2.做一个数据库中间件来判断来控制请求量
3.在代码层面吧热门行数据分割。
如何kill加锁的语句
我们使用show processlist可以查看当前所有的,但是大部分都是sleep的,因此不方便,但是我们可以使用 select blocking_pid from sys.schma_table_waits;查询出阻塞的线程id;
这个查询是查询表级锁的。
间隙锁
mysql为了解决可重复读的幻读问题,于是有了间隙锁,就是在加行锁的数据之间加一个新的锁,那就是间隙锁,依赖控制幻读问题产生。这个间隙锁,不仅仅只是在插入的时候有间隙锁的校验,还在你update的时候也有间隙锁的校验。间隙锁的冲突和行锁不一样,不是锁之间的冲突,间隙锁可以重复加,和间隙锁冲突的是往间隙锁里面添加内容。
加锁原则
1.加锁都是next-key锁,都是前开后闭原则。
2.只有访问到的对象才加锁。
3.给唯一索引加锁,next-key lock退化为行锁。
4.索引上的等值查询,向右遍历时且最后一个值不满足等值条件的时候,next-key lock 退化为间隙锁。
5.唯一索引的范围查询会访问到不满足条件的第一个值为止。
目前的mysql加锁都是基于上述规则。